﻿using System;

using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;

namespace iContactCfg
{
  class FieldsFactory
  {
    [DllImport("coredll.dll")]
    public static extern IntPtr FindWindow(string ClassName, string WindowName);
    [DllImport("coredll.dll")]
    public static extern bool DestroyWindow(IntPtr Window);

    enum MyFieldType
    {
      ftUnknown,
      ftSpecial,
      ftText,
      ftFileCombo,
      ftIntCombo,
      ftInt,
      ftCheckbox,
      ftSeparator,
      ftFile
    }

    class MyField
    {
      public string name;
      public string label;
      public string help;
      public MyFieldType type;

      public string value;

      public bool isNew = false;
    }

    class MySpecialField : MyField
    {
    }

    class MyTextField : MyField
    {
      public int length;
    }

    class MyFileComboField : MyField
    {
      public string filter;
    }

    class MyIntComboField : MyField
    {
      public ArrayList values;
    }

    class MyIntField : MyField
    {
      public int min;
      public int max;
    }

    class MyCheckboxField : MyField
    {
    }

    class MyFileField : MyField
    {
      public string filter;
    }

    class MySeparatorField : MyField
    {
    }

    class ControlMapItem
    {
      public MyField field;
      public Control control;
      public Control label;
      public Control help;
      public Control extra;
    }

    class SettingsMapItem
    {
      public string section;
      public string name;
      public string value;
      public ArrayList comments;

      public SettingsMapItem()
      {
      }

      public SettingsMapItem(SettingsMapItem item)
      {
        this.section = item.section;
        this.name = item.name;
        this.value = item.value;
        this.comments = item.comments;
      }
    }

    class LanguageMapItem
    {
      public string key;
      public string value;

      public LanguageMapItem()
      {
      }

      public LanguageMapItem(LanguageMapItem item)
      {
        this.key = item.key;
        this.value = item.value;
      }
    }

    // private
    Form form;
    string programPath;
    const string iContactExe = "iContact.exe";
    const string iniFile = "settings.ini";
    const string cfgFile = "cfg.dat";
    const string lngPrefix = "Lang\\";
    const string lngExt = ".clng";

    ArrayList settingsMap;
    ArrayList languageMap;
    ArrayList controlMap;
    string currentSection;

    const int nMarginTop = 5;
    const int nMarginLeft = 2;
    const int nItemSpacing = 3;
    static int nMarginRight;
    static int nMinLabelWidth;
    static int nMaxLabelWidth;
    static int nHelpButtonWidth;

    static string msgHelpHdr = "Description";
    static string msgWrongInt = "Numeric value required.";
    static string msgWrongIntHdr = "Wrong Value";
    static string msgWrongIntRange = "Range should be between {0} and {1}.";
    static string msgWrongIntRangeHdr = "Wrong Range";
    static string msgSaveOnExit = "Save changes before exit?";
    static string msgSaveOnExitHdr = "Exit";
    static string msgRestartContacts = "Restart iContact?";
    static string msgRestartContactsHdr = "Saved";
    static string msgAbout = "About:";
    const string msgVersion = "v7.0.6, (c) Avian Waves";

    // public
    public FieldsFactory(Form form)
    {
      this.form = form;
      this.programPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);

      System.Drawing.Graphics gra = this.form.CreateGraphics();
      System.Drawing.SizeF size = gra.MeasureString("?", this.form.Font);
      FieldsFactory.nHelpButtonWidth = (int)size.Width + 12;
      gra.Dispose();
      FieldsFactory.nMarginRight = this.form.Width / 20 + 2;

      int width = form.Width - FieldsFactory.nMarginLeft - FieldsFactory.nMarginRight - FieldsFactory.nHelpButtonWidth;
      FieldsFactory.nMinLabelWidth = width / 2;
      FieldsFactory.nMaxLabelWidth = width * 4 / 5;

      ReadSettings();
      ReadLanguage();
      CreateFields();
    }

    ~FieldsFactory()
    {
    }

    public bool HasConfigChanged()
    {
      string value;
      IEnumerator myEnum = this.settingsMap.GetEnumerator();
      while (myEnum.MoveNext())
      {
        SettingsMapItem item = (SettingsMapItem)myEnum.Current;

        value = GetGuiFieldValue(item.name);

        if (value.CompareTo(item.value) != 0)
          return true;
      }
      return false;
    }

    public void SaveSettings()
    {
      TextWriter data = new StreamWriter(this.programPath + "\\" + iniFile);

      currentSection = "";
      IEnumerator myEnum = this.settingsMap.GetEnumerator();
      while (myEnum.MoveNext())
      {
        SettingsMapItem item = (SettingsMapItem)myEnum.Current;

        item.value = GetGuiFieldValue(item.name);

        if (currentSection.CompareTo(item.section) != 0)
        {
          currentSection = item.section;
          data.WriteLine("[" + currentSection + "]");
        }

        if (item.comments != null)
        {
          for (int i = 0; i < item.comments.Count; i++)
            data.WriteLine(item.comments[i]);
        }
        data.WriteLine(item.name + "=" + item.value);
        data.WriteLine("");
      }
      data.Close();

      IntPtr wndContacts = FindWindow("iContact", null);
      if (wndContacts != null && wndContacts.ToInt32() != 0)
      {
        DialogResult res = MessageBox.Show(FieldsFactory.msgRestartContacts, FieldsFactory.msgRestartContactsHdr, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);
        if (res == DialogResult.Yes)
        {
          DestroyWindow(wndContacts);
          try
          {
            System.Diagnostics.Process.Start(this.programPath + "\\" + iContactExe, "");
          }
          catch (Exception e)
          {
            MessageBox.Show(e.Message);
          }
        }
      }
    }

    public void OnClosing()
    {
      DialogResult res = MessageBox.Show(FieldsFactory.msgSaveOnExit, FieldsFactory.msgSaveOnExitHdr, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);
      if (res == DialogResult.Yes)
      {
        SaveSettings();
      }
    }


    // private functions
    string GetFieldValue(string fieldName)
    {
      string lwrName = fieldName.ToLower();
      IEnumerator myEnum = this.settingsMap.GetEnumerator();
      while (myEnum.MoveNext())
      {
        SettingsMapItem item = (SettingsMapItem)myEnum.Current;

        if (item.name.ToLower().CompareTo(lwrName) == 0)
        {
          return item.value;
        }
      }

      return "";
    }

    string GetFieldValue(MyField field)
    {
      string lwrName = field.name.ToLower();
      IEnumerator myEnum = this.settingsMap.GetEnumerator();
      while (myEnum.MoveNext())
      {
        SettingsMapItem item = (SettingsMapItem)myEnum.Current;

        if (item.name.ToLower().CompareTo(lwrName) == 0)
        {
          return item.value;
        }
      }

      // not found, add new?
      field.isNew = true;
      switch (field.type)
      {
        case MyFieldType.ftText:
        case MyFieldType.ftFileCombo:
        case MyFieldType.ftIntCombo:
        case MyFieldType.ftInt:
        case MyFieldType.ftCheckbox:
        case MyFieldType.ftFile:
          if (field.name.Length > 0)
          {
            SettingsMapItem newSetting = new SettingsMapItem();
            newSetting.name = field.name;
            newSetting.section = "main";
            newSetting.value = field.value;
            this.settingsMap.Add(newSetting);
            return newSetting.value;
          }
          break;

        default:
          break;
      }

      return "";
    }

    string Localize(string key, string defValue)
    {
      string lwrKey = key.ToLower();
      IEnumerator myEnum = this.languageMap.GetEnumerator();
      while (myEnum.MoveNext())
      {
        LanguageMapItem item = (LanguageMapItem)myEnum.Current;

        if (item.key.ToLower().CompareTo(lwrKey) == 0)
        {
          return item.value;
        }
      }

      return defValue;
    }

    string GetGuiFieldValue(string fieldName)
    {
      IEnumerator myEnum = this.controlMap.GetEnumerator();
      while (myEnum.MoveNext())
      {
        ControlMapItem item = (ControlMapItem)myEnum.Current;

        if (item.control != null && item.field != null &&
            item.field.name.ToLower().CompareTo(fieldName.ToLower()) == 0)
        {
          switch (item.field.type)
          {
            case MyFieldType.ftText:
              return item.control.Text;
            case MyFieldType.ftFileCombo:
              return (((ComboBox)item.control).SelectedItem == null) ? "" : ((ComboBox)item.control).SelectedItem.ToString();
            case MyFieldType.ftIntCombo:
              return ((ComboBox)item.control).SelectedIndex.ToString();
            case MyFieldType.ftInt:
              return ((TextBox)item.control).Text;
            case MyFieldType.ftCheckbox:
              if (((CheckBox)item.control).Checked)
                return "yes";
              else
                return "no";
            case MyFieldType.ftFile:
              return item.control.Text;
            case MyFieldType.ftSeparator:
              break;
          }
        }
      }
      return "";
    }

    void ReadSettings()
    {
      this.settingsMap = new ArrayList();
      TextReader data = null;
      try
      {
        data = new StreamReader(this.programPath + "\\" + iniFile);
      }
      catch (FileNotFoundException)
      {
        throw new Exception("Unable to open " + this.programPath + "\\" + iniFile);
      }
      int lineNo = 0;
      SettingsMapItem item = new SettingsMapItem();

      while (GetIniPair(item, data, ref lineNo))
      {
        this.settingsMap.Add(new SettingsMapItem(item));
      }

      data.Close();
    }

    bool GetIniPair(SettingsMapItem item, TextReader data, ref int lineNo)
    {
      item.comments = new ArrayList();
      string line;
      while ((line = data.ReadLine()) != null)
      {
        lineNo++;
        if (line.Length > 0)
        {
          if (line.StartsWith("["))
          {
            currentSection = line.Substring(1, line.Length - 2);
          }
          else if (line.StartsWith("//") || line.StartsWith(";"))
          {
            item.comments.Add(line);
          }
          else
          {
            string[] pair = line.Split('=');
            if (pair.Length < 2)
            {
              throw new Exception("Error reading the " + iniFile + " at line " + lineNo + ": " + line);
            }
            item.section = currentSection;
            item.name = pair[0];
            item.value = pair[1];
            return true;
          }
        }
      }
      return false;
    }

    void ReadLanguage()
    {
      string lng = GetFieldValue("language");
      if (lng == "")
        lng = "english";
      string lngFile = this.programPath + "\\" + lngPrefix + lng + lngExt;

      TextReader data = null;
      try
      {
        data = new StreamReader(lngFile);
      }
      catch (FileNotFoundException)
      {
        lng = "english";
        lngFile = this.programPath + "\\" + lngPrefix + lng + lngExt;
        try
        {
          data = new StreamReader(lngFile);
        }
        catch (FileNotFoundException)
        {
          throw new Exception("Unable to open " + lngFile);
        }
      }

      this.languageMap = new ArrayList();
      int lineNo = 0;
      LanguageMapItem item = new LanguageMapItem();

      while (GetLngPair(item, data, lngFile, ref lineNo))
      {
        this.languageMap.Add(new LanguageMapItem(item));
      }

      data.Close();
    }

    bool GetLngPair(LanguageMapItem item, TextReader data, string fileName, ref int lineNo)
    {
      string line;
      while ((line = data.ReadLine()) != null)
      {
        lineNo++;
        if (line.Length > 0)
        {
          if (line.StartsWith("//") || line.StartsWith(";"))
          {
            // skip
          }
          else
          {
            string[] pair = line.Split('=');
            if (pair.Length < 2)
            {
              throw new Exception("Error reading the " + fileName + " at line " + lineNo + ": " + line);
            }
            item.key = pair[0];
            item.value = pair[1];
            return true;
          }
        }
      }
      return false;
    }

    void CreateFields()
    {
      TextReader data = null;
      try
      {
        data = new StreamReader(this.programPath + "\\" + cfgFile);
      }
      catch (FileNotFoundException)
      {
        throw new Exception("Unable to open " + this.programPath + "\\" + cfgFile);
      }

      TabControl tabs = new TabControl();
      tabs.Width = this.form.Width;
      tabs.Height = this.form.Height;
      this.form.Controls.Add(tabs);
      TabPage currentPage = null;

      this.controlMap = new ArrayList();
      int lineNo = 0;
      MyFieldType fieldType = MyFieldType.ftUnknown;

      int y = FieldsFactory.nMarginTop;

      while (GetPair("Field", ref fieldType, data, cfgFile, ref lineNo))
      {
        MyField field = null;
        switch (fieldType)
        {
          case MyFieldType.ftSpecial:
            field = new MySpecialField();
            break;
          case MyFieldType.ftText:
            field = new MyTextField();
            break;
          case MyFieldType.ftFileCombo:
            field = new MyFileComboField();
            break;
          case MyFieldType.ftIntCombo:
            field = new MyIntComboField();
            break;
          case MyFieldType.ftInt:
            field = new MyIntField();
            break;
          case MyFieldType.ftCheckbox:
            field = new MyCheckboxField();
            break;
          case MyFieldType.ftFile:
            field = new MyFileField();
            break;
          case MyFieldType.ftSeparator:
            field = new MySeparatorField();
            break;
        }
        if (field == null)
          throw new Exception("Unknown type in " + cfgFile + " at line " + lineNo + ": " + (int)fieldType);

        field.type = fieldType;

        // read name
        if (!GetPair("Name", ref field.name, data, cfgFile, ref lineNo))
          break;

        // read label
        if (!GetPair("Label", ref field.label, data, cfgFile, ref lineNo))
          break;

        field.label = Localize(field.name, field.label);
        field.value = GetFieldValue(field);

        if (field.type == MyFieldType.ftSpecial)
        {
          ProcessSpecialField(field);
          continue;
        }

        // read help
        field.help = Localize(field.name + "_Help", "");

        ControlMapItem newItem = new ControlMapItem();
        newItem.field = field;

        switch (field.type)
        {
          case MyFieldType.ftText:
            if (GetPair("Length", ref ((MyTextField)field).length, data, cfgFile, ref lineNo))
            {
              CreateTextField(newItem);
            }
            break;
          case MyFieldType.ftFileCombo:
            if (GetPair("Filter", ref ((MyFileComboField)field).filter, data, cfgFile, ref lineNo))
            {
              CreateFileComboField(newItem);
            }
            break;
          case MyFieldType.ftIntCombo:
            {
              string valuesStr = null;
              if (GetPair("Values", ref valuesStr, data, cfgFile, ref lineNo))
              {
                string[] values = valuesStr.Split(';');
                if (values.Length == 0)
                  throw new Exception("Empty values of " + field.name + " in " + cfgFile + " at line " + lineNo + ": " + valuesStr);

                ((MyIntComboField)field).values = new ArrayList(values.Length);
                for (int i = 0; i < values.Length; i++)
                  ((MyIntComboField)field).values.Add(Localize(values[i], (string)values[i].Clone()));

                CreateIntComboField(newItem);
              }
            }
            break;
          case MyFieldType.ftInt:
            if (GetPair("Min", ref ((MyIntField)field).min, data, cfgFile, ref lineNo))
            {
              if (GetPair("Max", ref ((MyIntField)field).max, data, cfgFile, ref lineNo))
              {
                CreateIntField(newItem);
              }
            }
            break;
          case MyFieldType.ftCheckbox:
            CreateCheckboxField(newItem);
            break;
          case MyFieldType.ftFile:
            if (GetPair("Filter", ref ((MyFileField)field).filter, data, cfgFile, ref lineNo))
            {
              CreateFileField(newItem);
            }
            break;
          case MyFieldType.ftSeparator:
            CreateSeparatorField(newItem);
            if (newItem.control != null)
            { // create new tab
              if (currentPage != null)
                currentPage.ResumeLayout(false);
              currentPage = new TabPage();
              currentPage.SuspendLayout();
              currentPage.AutoScroll = true;
              currentPage.Text = newItem.field.label;
              tabs.TabPages.Add(currentPage);
              y = FieldsFactory.nMarginTop;
            }
            break;
       
        }
        // avian: default values
        string def = "";
        if (GetPairDefault(ref def, data, cfgFile, ref lineNo))
        {
          if (field.isNew)
          {
            if (newItem.control is CheckBox)
            {
                ((CheckBox)newItem.control).Checked = false;
                try
                {
                    ((CheckBox)newItem.control).Checked = (Int32.Parse(def) == 1);
                }
                catch
                {
                }

                if (def.Trim() == "yes")
                {
                    ((CheckBox)newItem.control).Checked = true;
                }
            }
            else if (newItem.control is TextBox)
            {
              ((TextBox)newItem.control).Text = def;
            }
            else if (newItem.control is ComboBox)
            {
              ((ComboBox)newItem.control).Text = def;
            }
          }
        }

        if (newItem.control != null)
        {
          if (currentPage == null)
          {
            currentPage = new TabPage();
            currentPage.SuspendLayout();
            currentPage.AutoScroll = true;
            currentPage.Text = "General";
            tabs.TabPages.Add(currentPage);
            y = FieldsFactory.nMarginTop;
          }

          this.controlMap.Add(newItem);
          if (newItem.label != null)
          {
            currentPage.Controls.Add(newItem.label);
          }
          if (newItem.help != null)
          {
            currentPage.Controls.Add(newItem.help);
          }
          if (newItem.control != null)
          {
            currentPage.Controls.Add(newItem.control);
          }
          if (newItem.extra != null)
          {
            currentPage.Controls.Add(newItem.extra);
          }
          AlignItem(newItem, ref y);
        }
      }

      if (currentPage != null)
        currentPage.ResumeLayout(false);

      data.Close();

      //about
      AddAbout(tabs);

      if (this.controlMap.Count == 0)
        throw new Exception("No valid data found in " + cfgFile + ", please check its contents");
    }

    void AddAbout(TabControl tabs)
    {
      TabPage currentPage = new TabPage();
      currentPage.Text = "?";
      tabs.TabPages.Add(currentPage);
      int y = FieldsFactory.nMarginTop;

      ControlMapItem about = new ControlMapItem();
      about.field = new MySeparatorField();
      about.field.name = "About";
      about.field.type = MyFieldType.ftSeparator;
      about.field.label = FieldsFactory.msgAbout;
      CreateSeparatorField(about);
      if (about.control != null)
      {
        currentPage.Controls.Add(about.control);
      }
      this.controlMap.Add(about);
      AlignItem(about, ref y);

      ControlMapItem ver = new ControlMapItem();
      ver.field = new MySeparatorField();
      ver.field.name = "Version";
      ver.field.type = MyFieldType.ftSeparator;
      ver.field.label = FieldsFactory.msgVersion;
      CreateSeparatorField(ver);
      if (ver.control != null)
      {
        currentPage.Controls.Add(ver.control);
      }
      this.controlMap.Add(ver);
      AlignItem(ver, ref y);
    }

    bool GetPairDefault(ref string value, TextReader data, string file, ref int lineNo)
    {
      string line;
      if ((line = data.ReadLine()) != null)
      {
        lineNo++;
        if (line.Length > 0 && !line.StartsWith("//") && !line.StartsWith(";"))
        {
          string[] pair = line.Split('=');
          if (pair.Length < 2 ||
              pair[0].ToLower().CompareTo("default") != 0)
          {
            throw new Exception("Error reading the " + file + " at line " + lineNo + ": " + line);
          }
          value = pair[1];
          return true;
        }
      }
      return false;
    }

    bool GetPair(string name, ref string value, TextReader data, string file, ref int lineNo)
    {
      string line;
      while ((line = data.ReadLine()) != null)
      {
        lineNo++;
        if (line.Length > 0 && !line.StartsWith("//") && !line.StartsWith(";"))
        {
          string[] pair = line.Split('=');
          if (pair.Length < 2 ||
              pair[0].ToLower().CompareTo(name.ToLower()) != 0)
          {
            throw new Exception("Error reading the " + file + " at line " + lineNo + ": " + line);
          }
          value = pair[1];
          return true;
        }
      }
      return false;
    }

    bool GetPair(string name, ref int value, TextReader data, string file, ref int lineNo)
    {
      string svalue = "";
      if (GetPair(name, ref svalue, data, file, ref lineNo))
      {
        try
        {
          value = Int32.Parse(svalue);
          return true;
        }
        catch (Exception)
        {
          throw new Exception("Error value for " + name + " in " + file + " at line " + lineNo + ": " + svalue);
        }
      }
      return false;
    }

    bool GetPair(string name, ref MyFieldType value, TextReader data, string file, ref int lineNo)
    {
      string svalue = "";
      if (GetPair(name, ref svalue, data, file, ref lineNo))
      {
        string slvalue = svalue.ToLower();
        if (slvalue.CompareTo("special") == 0)
          value = MyFieldType.ftSpecial;
        else if (slvalue.CompareTo("text") == 0)
          value = MyFieldType.ftText;
        else if (slvalue.CompareTo("filecombo") == 0)
          value = MyFieldType.ftFileCombo;
        else if (slvalue.CompareTo("intcombo") == 0)
          value = MyFieldType.ftIntCombo;
        else if (slvalue.CompareTo("int") == 0)
          value = MyFieldType.ftInt;
        else if (slvalue.CompareTo("checkbox") == 0)
          value = MyFieldType.ftCheckbox;
        else if (slvalue.CompareTo("file") == 0)
          value = MyFieldType.ftFile;
        else if (slvalue.CompareTo("separator") == 0)
          value = MyFieldType.ftSeparator;
        else
          value = MyFieldType.ftUnknown;
        return true;
      }
      return false;
    }

    void ProcessSpecialField(MyField field)
    {
      if (field.name.ToLower().CompareTo("formtitle") == 0)
      {
        this.form.Text = field.label;
      }
      else if (field.name.ToLower().CompareTo("helphdr") == 0)
      {
        FieldsFactory.msgHelpHdr = field.label;
      }
      else if (field.name.ToLower().CompareTo("wrongint") == 0)
      {
        FieldsFactory.msgWrongInt = field.label;
      }
      else if (field.name.ToLower().CompareTo("wronginthdr") == 0)
      {
        FieldsFactory.msgWrongIntHdr = field.label;
      }
      else if (field.name.ToLower().CompareTo("wrongintrange") == 0)
      {
        FieldsFactory.msgWrongIntRange = field.label;
      }
      else if (field.name.ToLower().CompareTo("wrongintrangehdr") == 0)
      {
        FieldsFactory.msgWrongIntRangeHdr = field.label;
      }
      else if (field.name.ToLower().CompareTo("saveonexit") == 0)
      {
        FieldsFactory.msgSaveOnExit = field.label;
      }
      else if (field.name.ToLower().CompareTo("saveonexithdr") == 0)
      {
        FieldsFactory.msgSaveOnExitHdr = field.label;
      }
      else if (field.name.ToLower().CompareTo("restartcontacts") == 0)
      {
        FieldsFactory.msgRestartContacts = field.label;
      }
      else if (field.name.ToLower().CompareTo("restartcontactshdr") == 0)
      {
        FieldsFactory.msgRestartContactsHdr = field.label;
      }
      else if (field.name.ToLower().CompareTo("about") == 0)
      {
        FieldsFactory.msgAbout = field.label;
      }
      else if (field.name.ToLower().CompareTo("okbutton") == 0)
      {
        IEnumerator myEnum = this.form.Menu.MenuItems.GetEnumerator();
        while (myEnum.MoveNext())
        {
          MenuItem item = (MenuItem)myEnum.Current;
          if (item != null && item.Text.ToLower().CompareTo("ok") == 0)
          {
            item.Text = field.label;
            break;
          }
        }
      }
      else if (field.name.ToLower().CompareTo("cancelbutton") == 0)
      {
        IEnumerator myEnum = this.form.Menu.MenuItems.GetEnumerator();
        while (myEnum.MoveNext())
        {
          MenuItem item = (MenuItem)myEnum.Current;
          if (item != null && item.Text.ToLower().CompareTo("cancel") == 0)
          {
            item.Text = field.label;
            break;
          }
        }
      }
    }

    void AlignItem(ControlMapItem item, ref int y)
    {
      int left = 0;
      int lheight = AlignLabel(item.label, item.control, y);

      if (item.field.type == MyFieldType.ftCheckbox ||
          item.field.type == MyFieldType.ftSeparator)
        left = 10;
      else
        left = FieldsFactory.nMinLabelWidth;

      AlignControl(item.control, y, left, lheight);
      if (lheight > item.control.Height)
        y += lheight;
      else
        y += item.control.Height + FieldsFactory.nItemSpacing;

      if (item.extra != null)
      {
        if (item.field.type == MyFieldType.ftFile)
        {
          item.control.Width -= FieldsFactory.nHelpButtonWidth;
          item.extra.Left = item.control.Right;
          item.extra.Width = FieldsFactory.nHelpButtonWidth;
          item.extra.Top = item.control.Top;
          item.extra.Height = item.control.Height;
        }
      }

      AlignHelpButton(item.help, item.control.Top, item.control.Height);
    }

    int AlignLabel(Control label, Control control, int y)
    {
      if (label == null)
        return 0;

      System.Drawing.Graphics gra = this.form.CreateGraphics();
      System.Drawing.SizeF size = gra.MeasureString(label.Text, label.Font);
      int width = (int)size.Width + 5;
      label.Height = (int)size.Height + 3;
      gra.Dispose();
      if (width > FieldsFactory.nMinLabelWidth)
      {
        label.Top = y;
        label.Height = (width / FieldsFactory.nMinLabelWidth + 1) * label.Height;
      }
      else
      {
        label.Top = y + (control.Height - label.Height) / 2;
      }
      label.Left = FieldsFactory.nMarginLeft;
      label.Width = FieldsFactory.nMinLabelWidth;

      return label.Height;
    }

    void AlignHelpButton(Control helpButton, int controlTop, int controlHeight)
    {
      if (helpButton == null)
        return;

      helpButton.Top = controlTop;
      helpButton.Left = this.form.Width - FieldsFactory.nMarginRight - FieldsFactory.nHelpButtonWidth;
      helpButton.Width = FieldsFactory.nHelpButtonWidth;
      helpButton.Height = controlHeight;
    }

    void AlignControl(Control control, int y, int left, int labelHeight)
    {
      if (control == null)
        return;

      control.Top = y;
      if (labelHeight > control.Height)
        control.Top += (labelHeight - control.Height) / 2;
      control.Left = FieldsFactory.nMarginLeft + left;
      control.Width = this.form.Width - FieldsFactory.nMarginRight - FieldsFactory.nHelpButtonWidth - control.Left;
      System.Drawing.Graphics gra = this.form.CreateGraphics();
      System.Drawing.SizeF size = gra.MeasureString("?", control.Font);
      control.Height = (int)size.Height + 5;
      gra.Dispose();
    }

    Control CreateLabel(string text)
    {
      Label newControl = new Label();
      newControl.Text = text;
      newControl.TextAlign = System.Drawing.ContentAlignment.TopRight;
      return newControl;
    }

    Control CreateHelpButton(MyField field)
    {
      if (field == null || field.help.Length == 0)
        return null;

      Button newControl = new Button();
      newControl.Text = "?";
      newControl.Tag = field;
      newControl.Click += this.HelpButton_Click;
      return newControl;
    }

    private void HelpButton_Click(Object sender, System.EventArgs e)
    {
      Button button = (Button)sender;
      if (button == null)
        return;
      MyField field = (MyField)button.Tag;
      if (field == null)
        return;
      if (field.help.Length > 0)
        MessageBox.Show(field.help, FieldsFactory.msgHelpHdr, MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);
    }

    void CreateTextField(ControlMapItem c)
    {
      MyTextField f = (MyTextField)c.field;

      c.label = CreateLabel(f.label);
      c.help = CreateHelpButton(f);

      TextBox newControl = new TextBox();
      newControl.MaxLength = f.length;
      newControl.Text = f.value;
      c.control = newControl;
    }

    void CreateFileComboField(ControlMapItem c)
    {
      MyFileComboField f = (MyFileComboField)c.field;

      c.label = CreateLabel(f.label);
      c.help = CreateHelpButton(f);

      string lwrValue = f.value.ToLower();
      int selIndex = -1;
      ComboBox newControl = new ComboBox();
      // fill the combo
      DirectoryInfo dirInfo = new DirectoryInfo(this.programPath);
      FileInfo[] files = dirInfo.GetFiles(f.filter);
      IEnumerator myEnum = files.GetEnumerator();
      while (myEnum.MoveNext())
      {
        FileInfo fiNext = (FileInfo)myEnum.Current;
        string name = fiNext.Name.Substring(0, fiNext.Name.IndexOf(fiNext.Extension));
        int idx = newControl.Items.Add(name);
        if (name.ToLower().CompareTo(lwrValue) == 0)
          selIndex = idx;
      }

      newControl.SelectedIndex = selIndex;
      c.control = newControl;
    }

    void CreateIntComboField(ControlMapItem c)
    {
      MyIntComboField f = (MyIntComboField)c.field;

      c.label = CreateLabel(f.label);
      c.help = CreateHelpButton(f);

      ComboBox newControl = new ComboBox();

      IEnumerator myEnum = f.values.GetEnumerator();
      while (myEnum.MoveNext())
      {
        string value = (string)myEnum.Current;
        newControl.Items.Add(value);
      }

      try
      {
        newControl.SelectedIndex = Int32.Parse(f.value);
      }
      catch (Exception)
      {
      }
      c.control = newControl;
    }

    void CreateIntField(ControlMapItem c)
    {
      MyIntField f = (MyIntField)c.field;

      c.label = CreateLabel(f.label);
      c.help = CreateHelpButton(f);

      TextBox newControl = new TextBox();
      newControl.Text = f.value;
      newControl.TextChanged += this.intFieldChangedEventHandler;
      c.control = newControl;
    }

    private void intFieldChangedEventHandler(object sender, EventArgs e)
    {
      TextBox control = (TextBox)sender;
      IEnumerator myEnum = this.controlMap.GetEnumerator();
      while (myEnum.MoveNext())
      {
        ControlMapItem item = (ControlMapItem)myEnum.Current;
        if (item.control == control)
        {
          if (control.Text.Length > 0)
          {
            try
            {
              int value = Int32.Parse(control.Text);
              MyIntField field = (MyIntField)item.field;
              if (value < field.min || value > field.max)
              {
                MessageBox.Show(string.Format(FieldsFactory.msgWrongIntRange, field.min, field.max),
                      FieldsFactory.msgWrongIntRangeHdr, MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
              }
            }
            catch (Exception)
            {
              MessageBox.Show(FieldsFactory.msgWrongInt, FieldsFactory.msgWrongIntHdr, MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
            }
          }
          break;
        }
      }
    }

    void CreateCheckboxField(ControlMapItem c)
    {
      MyCheckboxField f = (MyCheckboxField)c.field;

      c.help = CreateHelpButton(f);

      CheckBox newControl = new CheckBox();
      newControl.Text = f.label;

      newControl.Checked = false;

      try
      {
        newControl.Checked = (Int32.Parse(f.value) == 1);
      }
      catch (Exception)
      {
      }
        
      if (f.value != null && f.value.Trim() == "yes")
      {
          newControl.Checked = true;
      }

      c.control = newControl;
    }

    void CreateFileField(ControlMapItem c)
    {
      MyFileField f = (MyFileField)c.field;

      c.label = CreateLabel(f.label);
      c.help = CreateHelpButton(f);

      TextBox newControl = new TextBox();
      newControl.Text = f.value;
      c.control = newControl;

      Button btn = new Button();
      btn.Text = "...";
      btn.Tag = c;
      btn.Click += this.FileButton_Click;
      c.extra = btn;
    }

    private void FileButton_Click(Object sender, System.EventArgs e)
    {
      Button button = (Button)sender;
      if (button == null)
        return;
      ControlMapItem item = (ControlMapItem)button.Tag;
      if (item == null)
        return;
      if (item.field.type == MyFieldType.ftFile)
      {
        MyFileField fileField = (MyFileField)item.field;
        OpenFileDialog fd = new OpenFileDialog();
        fd.Filter = fileField.filter;
        fd.FilterIndex = 1;
        if (fd.ShowDialog() == DialogResult.OK)
        {
          item.control.Text = fd.FileName;
        }
      }
    }

    void CreateSeparatorField(ControlMapItem c)
    {
      MySeparatorField f = (MySeparatorField)c.field;

      Label newControl = new Label();
      newControl.Text = f.label;
      newControl.TextAlign = System.Drawing.ContentAlignment.TopLeft;
      newControl.Font = new System.Drawing.Font(newControl.Font.Name,
                    newControl.Font.Size, System.Drawing.FontStyle.Bold);
      newControl.ForeColor = System.Drawing.Color.DarkBlue;
      c.control = newControl;
    }
  }
}
